﻿using System;
using System.Data;
using System.Data.Common;
using System.Reflection;
using System.Collections.Generic;

//自定义命名空间
using DreamCube.Foundation.Basic.Utility;

namespace DreamCube.Framework.DataAccess.Basic
{
    /// <summary>
    /// 基础数据表对象，派生类可以直接继承这个类，这个类提供的默认保存方法是收集此类的属性与数据表列对应的关系进行保存
    /// </summary>
    public class DBObject
    {
        #region "字段"

        /// <summary>
        /// 数据库字段
        /// </summary>
        protected Database db;

        /// <summary>
        /// 主键
        /// </summary>
        protected List<String> primaryKeys;

        /// <summary>
        /// 自增列(标识列)
        /// </summary>
        protected String identity;

        /// <summary>
        /// 数据行对象
        /// </summary>
        private DataRow innerDataRow;

        /// <summary>
        /// 对应的数据表的架构信息
        /// </summary>
        protected DataTable tableSchema;

        /// <summary>
        /// 对应数据库表名
        /// </summary>
        protected String tableName = String.Empty;

        /// <summary>
        /// 对象的状态
        /// </summary>
        protected DBObjectState DBObjectState
        {
            get;
            private set;
        }

        #endregion

        #region "属性"
        
        /// <summary>
        /// 对应的数据库表名
        /// </summary>
        public String TableName
        {
            get { return this.tableName; }
        }

        /// <summary>
        /// 返回数据库字段
        /// </summary>
        public virtual Database DB
        {
            get { return db; }
        }

        /// <summary>
        /// 是否是一条新纪录
        /// </summary>
        /// <returns></returns>
        internal protected Boolean IsNewRecord
        {
            get 
            {
                if(DBObjectState == Basic.DBObjectState.Created)return true;
                return false;
            }
        }

        /// <summary>
        /// 获取主键列
        /// </summary>
        internal protected List<String> PrimaryKeys
        {
            get
            {
                if (this.primaryKeys == null)
                {
                    this.primaryKeys = new List<String>();
                    for (Int32 i = 0, j = this.tableSchema.Rows.Count; i < j; i++)
                    {
                        if (MyObject.ToBoolean(this.tableSchema.Rows[i]["IsKey"]))
                            this.primaryKeys.Add(this.tableSchema.Rows[i]["ColumnName"].ToString());
                    }
                }
                return this.primaryKeys;
            }
        }

        /// <summary>
        /// 自增列
        /// </summary>
        public String Identity
        {
            get
            {
                if (this.identity == null)
                {
                    for (Int32 i = 0, j = this.tableSchema.Rows.Count; i < j; i++)
                    {
                        if (MyObject.ToBoolean(this.tableSchema.Rows[i]["IsAutoIncrement"]))
                        {
                            this.identity = this.tableSchema.Rows[i]["ColumnName"].ToString();
                            break;
                        }
                    }
                }
                return this.identity;
            }
        }

        #endregion

        #region "公共方法"

        /// <summary>
        /// 提供给派生类调用这个方法
        /// </summary>
        /// <param name="db"></param>
        /// <param name="tableName"></param>
        public DBObject(String tableName)
            : this(null, tableName)
        { }

        /// <summary>
        /// 必须调用的构造函数
        /// </summary>
        /// <param name="db"></param>
        /// <param name="tableName"></param>
        public DBObject(Database db, String tableName)
        {
            //记录到受保护的字段上，方便给派生类重写TableName属性
            this.tableName = tableName;
            this.db = db;
            this.DBObjectState = Basic.DBObjectState.Created;
            if (String.IsNullOrEmpty(this.TableName))
            {
                if (String.IsNullOrEmpty(tableName))
                    throw new Exception("没有给DBObject对象提供合法的数据表名，创建对象失败");
            }
            else
            {
                this.tableName = this.TableName;
            }

            //初始化表架构信息
            this.InitialTableSchema(this.tableName);
            this.InitialInnerDataRow();
        }

        /// <summary>
        /// 获取字段值
        /// </summary>
        /// <param name="itemName"></param>
        /// <returns></returns>
        public Object GetItemValue(String itemName)
        {
            if (this.innerDataRow.Table.Columns.Contains(itemName))
                return this.innerDataRow[itemName];
            return null;
        }

        /// <summary>
        /// 设置字段值
        /// </summary>
        /// <param name="itemName"></param>
        /// <param name="value"></param>
        public void SetItemValue(String itemName, Object value)
        {
            if (this.innerDataRow.Table.Columns.Contains(itemName))
            {
                if (this.DBObjectState != Basic.DBObjectState.Created)
                {
                    if (this.DBObjectState == DBObjectState.UnChanged)
                        this.DBObjectState = DBObjectState.Modified;
                }
                this.innerDataRow[itemName] = value == null ? System.DBNull.Value : value;
            }
        }

        /// <summary>
        /// 保存方法
        /// </summary>
        /// <param name="command">命令对象，通过换入一个命令对象，可以控制操作在事务中执行</param>
        /// <returns></returns>
        public Boolean SaveToDB(DbCommand command = null)
        {
            if (this.DBObjectState == DBObjectState.UnChanged) return true;
            if (this.DBObjectState == DBObjectState.Modified)
                return this.UpdateDataToDb(command);
            else if (this.DBObjectState == DBObjectState.Created)
                return this.InsertNewRecordToDb(command);
            return false;
        }

        #endregion

        #region "受保护方法"

        /// <summary>
        /// 判断是否是主键列
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns></returns>
        protected Boolean IsPrimaryKeyColumn(String columnName)
        {
            if (this.PrimaryKeys == null) return false;
            for (Int32 i = 0, j = this.PrimaryKeys.Count; i < j; i++)
            {
                if (String.Compare(this.PrimaryKeys[i], columnName, true) == 0) return true;
            }
            return false;
        }

        /// <summary>
        /// 判断是否是自增列
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns></returns>
        protected Boolean IsIdentity(String columnName)
        {
            if (String.IsNullOrEmpty(this.Identity)) return false;
            return String.Compare(this.Identity, columnName, true) == 0;
        }

        /// <summary>
        /// 添加新记录到数据库中
        /// </summary>
        /// <param name="command">命令对象，通过换入一个命令对象，可以控制操作在事务中执行</param>
        /// <returns></returns>
        protected Boolean InsertNewRecordToDb(DbCommand command = null)
        {
            if (command == null) command = DB.CreateCommand();
            List<String> columnValuePair = new List<String>();
            DataColumnCollection cols = this.innerDataRow.Table.Columns;
            for (Int32 i = 0, j = cols.Count; i < j; i++)
            {
                String columnName = cols[i].ColumnName;
                if (IsIdentity(columnName) || IsPrimaryKeyColumn(columnName)) continue;
                String paramName = DB.FormatParameterName(columnName);
                columnValuePair.Add(paramName);
                DB.AddInParameter(command, paramName, GetColumnDbType(columnName), this.innerDataRow[columnName]);
            }
            String sql = String.Format("insert into {0} values({1})",
                                            this.TableName,
                                            MyEnumerable.JoinEx(columnValuePair, ","));

            Int32 result = 0;
            command.CommandText = sql;
            //获取自增列的值回来
            if (!String.IsNullOrEmpty(this.Identity))
            {
                sql += ";select @identityReturnValX = @@identity;";
                DB.AddOutParameter(command, "@identityReturnValX", DbType.Int32);
                result = DB.ExecuteNonQuery(command);
                //设置自增列的值
                this.SetItemValue(this.Identity, command.Parameters["@identityReturnValX"].Value);
            }
            else
            {
                result = DB.ExecuteNonQuery(command);
            }
            if (result > 0) this.DBObjectState = DBObjectState.UnChanged;
            return result > 0;
        }

        /// <summary>
        /// 更新记录
        /// </summary>
        /// <param name="command">命令对象，通过换入一个命令对象，可以控制操作在事务中执行</param>
        /// <returns></returns>
        protected Boolean UpdateDataToDb(DbCommand command = null)
        {
            if (this.PrimaryKeys == null || this.PrimaryKeys.Count == 0)
                throw new Exception(String.Format(Properties.Resources.ExceptionTableNotFound, this.TableName));
            if (command == null) command = DB.CreateCommand();
            List<String> columnValuePair = new List<String>();
            List<String> whereValuePair = new List<String>();
            DataColumnCollection cols = this.innerDataRow.Table.Columns;
            for (Int32 i = 0, j = cols.Count; i < j; i++)
            {
                String paramName = DB.FormatParameterName(cols[i].ColumnName);
                if (IsPrimaryKeyColumn(cols[i].ColumnName))
                {
                    whereValuePair.Add(cols[i].ColumnName + "=" + paramName);
                    Object keyValue = this.innerDataRow[cols[i].ColumnName];
                    //如果主键值都未null，则可以直接返回了，更新操作不可以没有对应的主键的
                    if(keyValue == DBNull.Value)return false;
                    DB.AddInParameter(command, paramName, GetColumnDbType(cols[i].ColumnName), keyValue);
                    continue;
                }
                if (IsIdentity(cols[i].ColumnName)) continue;
                columnValuePair.Add(cols[i].ColumnName + "=" + paramName);
                DB.AddInParameter(command, paramName, GetColumnDbType(cols[i].ColumnName), this.innerDataRow[cols[i].ColumnName]);
            }
            String sql = String.Format("update {0} set {1} where {2}",
                                            this.TableName,
                                            MyEnumerable.JoinEx(columnValuePair, ","),
                                            MyEnumerable.JoinEx(whereValuePair, ","));
            command.CommandText = sql;
            Int32 result = DB.ExecuteNonQuery(command);
            if (result > 0) this.DBObjectState = DBObjectState.UnChanged;
            return result > 0;
        }

        /// <summary>
        /// 根据表名和列名获取数据类型
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns></returns>
        protected DbType GetColumnDbType(String columnName)
        {
            DbType type = DbType.String;
            for (Int32 i = 0, j = this.tableSchema.Rows.Count; i < j; i++)
            {
                if (String.Compare(this.tableSchema.Rows[i]["ColumnName"].ToString(), columnName) == 0)
                {
                    MyEnum.TryToEnum(this.tableSchema.Rows[i]["DataTypeName"].ToString(), out type, false);
                }
            }
            return type;
        }

        /// <summary>
        /// 初始化表架构信息
        /// </summary>
        protected void InitialTableSchema(String tableName)
        {
            this.tableSchema = DB.GetTableSchema(tableName);
            if (this.tableSchema == null)
            {
                throw new Exception(String.Format(
                                    Properties.Resources.ExceptionTableNotFound,
                                    this.DB.ConnectionString.ToStringNoCredentials(), tableName));
            }
        }

        /// <summary>
        /// 初始化数据行
        /// </summary>
        protected void InitialInnerDataRow()
        {
            DataTable table = new DataTable();
            for (Int32 i = 0, j = this.tableSchema.Rows.Count; i < j; i++)
                table.Columns.Add(this.tableSchema.Rows[i]["ColumnName"].ToString());
            this.innerDataRow = table.NewRow();
        }

        #endregion
    }
}
